/**
@author		Jong Min, Lee
@date		  02/08/2014
@brief		Hidden toolbar

copyright(c) Tae Young Soft. All right reserved. 2014
*/

#include "stdafx.h"
#include "Taurus_Viewer/HiddenToolbar.h"

#include <algorithm>

#include "Taurus_Util/Errors.h"

IMPLEMENT_DYNAMIC(CHiddenToolbar, CWnd)

CHiddenToolbar::CHiddenToolbar()
: m_position(ToolbarPositionLeft)
, m_theResType(MonitorResolution_Normal)
, m_theMonitorIndex(0)
, m_hoverItem(NULL)
, m_pushedItem(NULL)
{
  m_theDrawManager = &m_service;
  m_theRearrangeManager = &m_service;
  m_theBehaviorManager = &m_service;
}

CHiddenToolbar::~CHiddenToolbar()
{
}

void CHiddenToolbar::setDrawManager(CHiddenToolbarDraw* manager)
{
  m_theDrawManager = (manager != NULL ? manager : &m_service);
}

CHiddenToolbarDraw* CHiddenToolbar::getDrawManager() const
{
  return m_theDrawManager;
}

void CHiddenToolbar::setRearrangeManager(IHiddenToolbarRearrange* manager)
{
  m_theRearrangeManager = (manager != nullptr ? manager : &m_service);

}

IHiddenToolbarRearrange* CHiddenToolbar::getRearrangeManager() const
{
  return m_theRearrangeManager;
}

void CHiddenToolbar::setBehaviorManager(IHiddenToolbarBehavior* manager)
{
  m_theBehaviorManager = (manager != nullptr ? manager : &m_service);
}

IHiddenToolbarBehavior* CHiddenToolbar::getBehaviorManager() const
{
  return m_theBehaviorManager;
}

DWORD CHiddenToolbar::setPosition(ToolbarPosition newPos, BOOL redraw)
{
  if(newPos <= ToolbarPositionFirstInvalid &&
     newPos >= ToolbarPositionLastInvalid)
     return ERROR_INVALID_PARAMETER;

  m_position = newPos;
  rearrangne();
  if(::IsWindow(this->GetSafeHwnd()) && redraw)
    Invalidate();

  return ERROR_SUCCESS;
}

const CHiddenToolbarItem* CHiddenToolbar::getItem(unsigned index) const
{
  if(index >= getNumItems())
    return nullptr;

  return m_items[index];
}

const CHiddenToolbarItem* CHiddenToolbar::getHoverItem() const
{
  return m_hoverItem;
}

const CHiddenToolbarItem* CHiddenToolbar::getPushedItem() const
{
  return m_pushedItem;
}

const CHiddenToolbarItem* CHiddenToolbar::hitTest(const CPoint& point)
{
  return m_theBehaviorManager->hitTest(*this,
                                       m_service,
                                       point);
}

int CHiddenToolbar::getIndex(const CHiddenToolbarItem* item) const
{
  int index = -1;
  for(unsigned i = 0; i < m_items.size(); ++i)
  {
    if(m_items[i] == item)
    {
      index = static_cast<int>(i);
      break;
    }
  }
  return index;
}

BOOL CHiddenToolbar::isExist(const CHiddenToolbarItem* item) const
{
  auto itemPos = find_if(m_items.cbegin(), m_items.cend(),
    [&item](const CHiddenToolbarItem* myItem)
  {
    return (item == myItem);
  });

  return itemPos != m_items.cend();
}

DWORD CHiddenToolbar::createItems()
{
  _TRY_BEGIN
  m_items.reserve(2);//thumbnail, toolbox
  _CATCH_ALL
  return ERROR_NOT_ENOUGH_MEMORY;
  _CATCH_END

  //toolbox
  CHiddenToolbarItem* toolboxItem = NULL;
  _TRY_ALLOC(toolboxItem, CHiddenToolbarItem);
  if(toolboxItem == nullptr)
  {
    m_items.clear();
    return ERROR_NOT_ENOUGH_MEMORY;
  }
  toolboxItem->m_text = _T("Toolbox");
  toolboxItem->rect = CRect(0,0,0,0);
  toolboxItem->length = 0;
  toolboxItem->uID = SHOW_TOOLBOX_ITEM;
  m_items.push_back(toolboxItem);

  //thumbnail
  CHiddenToolbarItem* thumbnailItem = NULL;
  _TRY_ALLOC(thumbnailItem, CHiddenToolbarItem);
  if(thumbnailItem == nullptr)
  {
    removeAllItems();
    return ERROR_NOT_ENOUGH_MEMORY;
  }
  thumbnailItem->m_text = _T("Thumbnail");
  thumbnailItem->rect = CRect(0,0,0,0);
  thumbnailItem->uID = SHOW_THUMBNAIL_ITEM;
  m_items.push_back(thumbnailItem);

  return ERROR_SUCCESS;
}

void CHiddenToolbar::removeAllItems()
{
  for(unsigned i = 0; i < m_items.size(); ++i)
    _DELETE(m_items[i]);
  m_items.clear();
}

void CHiddenToolbar::rearrangne()
{
  unsigned nNumItems = getNumItems();
  if(nNumItems == 0) return;

  calcItemsLength();
  calcItemsRect();
}

void CHiddenToolbar::calcItemsLength()
{
  CRect clientRect;
  GetClientRect(&clientRect);

  BOOL isVertical = CToolbarLayout::isVertical(m_position);

  int itemsLength = isVertical ? clientRect.Height() : clientRect.Width();

  CRect itemMargin = getItemMargin();

  int nNumItems = static_cast<int>(getNumItems());
  int itemLength = 0;
  if(isVertical)
    itemLength = (itemsLength / nNumItems) -
                 ((itemMargin.top + itemMargin.bottom) * nNumItems);
  else
    itemLength = (itemsLength / nNumItems) -
                 ((itemMargin.left + itemMargin.right) * nNumItems);
  
  for(unsigned i = 0; i < m_items.size(); ++i)
    m_items[i]->length = itemLength;
}

void CHiddenToolbar::calcItemsRect()
{
  CRect rcClient;
  GetClientRect(&rcClient);
  CRect itemMargin = getItemMargin();
  int pos = 0;
  BOOL isVertical = CToolbarLayout::isVertical(m_position);
  if(isVertical)
  {
    for(unsigned i = 0; i < m_items.size(); ++i)
    {
      m_items[i]->rect = rcClient;
      m_items[i]->rect.top = pos;
      m_items[i]->rect.bottom = pos += (itemMargin.top + m_items[i]->length + itemMargin.bottom);
      m_items[i]->rect.DeflateRect(0, itemMargin.top, 0, itemMargin.bottom);
    }
  }
  else
  {
    for(unsigned i = 0; i < m_items.size(); ++i)
    {
      m_items[i]->rect = rcClient;
      m_items[i]->rect.left = pos;
      m_items[i]->rect.right = pos += (itemMargin.left + m_items[i]->length + itemMargin.right);
      m_items[i]->rect.DeflateRect(itemMargin.left, 0, itemMargin.right, 0);
    }
  }
}

CRect CHiddenToolbar::getItemMargin()
{
  return m_theRearrangeManager->getTabMargin(*this, m_service);
}

void CHiddenToolbar::assignHoverItem(const CPoint& point)
{
  const CHiddenToolbarItem* oldHoverItem = m_hoverItem;
  m_hoverItem = hitTest(point);

  if(oldHoverItem != m_hoverItem)
  {
    ::RedrawWindow(m_hWnd, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW | RDW_NOCHILDREN);
  }

  TRACKMOUSEEVENT tme = {sizeof(TRACKMOUSEEVENT), TME_LEAVE, m_hWnd, 0};
  ::TrackMouseEvent(&tme);
}

BOOL CHiddenToolbar::validateCreateParams(const CreateParam& params)
{
  if(params.theResType != MonitorResolution_Normal &&
     params.theResType != MonitorResolution_3M &&
     params.theResType != MonitorResolution_5M)
     return FALSE;

  return TRUE;
}

CHiddenToolbar::CPaintDC::CPaintDC(CDC* dstDC, const CWnd& wndDst)
{
  ASSERT(dstDC != nullptr && dstDC->m_hDC != nullptr);
  ASSERT(::IsWindow(wndDst.GetSafeHwnd()));

  wndDst.GetClientRect(&m_rcDst);
  m_success = init(dstDC);
}

CHiddenToolbar::CPaintDC::CPaintDC(CDC* dstDC, const CRect& rcDst)
{
  ASSERT(dstDC != nullptr && dstDC->m_hDC != nullptr);

  m_rcDst = rcDst;
  m_success = init(dstDC);
}

CHiddenToolbar::CPaintDC::~CPaintDC()
{
  if(m_success)
  {
    m_dcDst->BitBlt(m_rcDst.left, m_rcDst.top, m_rcDst.Width(), m_rcDst.Height(),
                    this, m_rcDst.left, m_rcDst.top, SRCCOPY);
    SelectObject(m_oldBmp);
  }
  else
    Detach();
}

BOOL CHiddenToolbar::CPaintDC::init(CDC* dcDst)
{
  m_dcDst = dcDst;

  BOOL res = isDoubleBuffering() &&
             CreateCompatibleDC(m_dcDst) &&
             m_bmpSrc.CreateCompatibleBitmap(m_dcDst, m_rcDst.Width(), m_rcDst.Height());
  if(res)
    m_oldBmp = SelectObject(&m_bmpSrc);
  else
  {
    if(m_hDC != nullptr) DeleteDC();
    if(m_bmpSrc.m_hObject != nullptr) m_bmpSrc.DeleteObject();
    Attach(m_dcDst->m_hDC);
  }
  SetBkMode(TRANSPARENT);
  return res;
}

CRect CHiddenToolbar::CSerivce::getTabMargin(CHiddenToolbar& toolbar, IHiddenToolbarRearrange& base)
{
  return CRect(0, 0, 0, 0);
}

const CHiddenToolbarItem* CHiddenToolbar::CSerivce::hitTest(CHiddenToolbar& toolbar, 
                                                            IHiddenToolbarBehavior& base, 
                                                            const CPoint& point)
{
  CRect rcClient;
  toolbar.GetClientRect(&rcClient);
  if(rcClient.PtInRect(point))
  {
    unsigned numItems = toolbar.getNumItems();
    for(unsigned i = 0; i < numItems; ++i)
    {
      const CHiddenToolbarItem* item = toolbar.getItem(i);
      if(item->rect.PtInRect(point))
        return item;
    }
  }

  return nullptr;
}

void CHiddenToolbar::CSerivce::drawBK(CHiddenToolbar& toolbar, CDC& dc)
{
  CRect rcClient;
  toolbar.GetClientRect(&rcClient);

  COLORREF clr = RGB(45, 45, 48);
  dc.FillSolidRect(&rcClient, clr);
}

void CHiddenToolbar::CSerivce::drawItem(CHiddenToolbar& toolbar, CDC& dc, const CHiddenToolbarItem& item)
{
  drawItemBK(toolbar, dc, item);

  drawItemTitle(toolbar, dc, item);
}

void CHiddenToolbar::CSerivce::drawItemBK(CHiddenToolbar& toolbar, CDC& dc, const CHiddenToolbarItem& item)
{
  BOOL pushed = (toolbar.getPushedItem() == &item);
  BOOL hover = (toolbar.getHoverItem() == &item);

  COLORREF clr;
  if(pushed)
    clr = RGB(0, 122, 204);
  else if(hover)
    clr = RGB(28, 151, 234);
  else
    clr = RGB(45, 45, 48);

  dc.FillSolidRect(&(item.rect), clr);
}

void CHiddenToolbar::CSerivce::drawItemTitle(CHiddenToolbar& toolbar, CDC& dc, const CHiddenToolbarItem& item)
{
}

BEGIN_MESSAGE_MAP(CHiddenToolbar, CWnd)
  ON_WM_CREATE()
  ON_WM_DESTROY()
  ON_WM_SIZE()
  ON_WM_PAINT()
  ON_WM_LBUTTONDOWN()
  ON_WM_MOUSEMOVE()
  ON_WM_LBUTTONUP()
  ON_MESSAGE(WM_MOUSELEAVE, OnMouseLeave)
  ON_WM_ERASEBKGND()
END_MESSAGE_MAP()



// CHiddenToolbar message handlers

int CHiddenToolbar::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
  if(CWnd::OnCreate(lpCreateStruct) == -1)
    return -1;

  CreateParam* createParam = reinterpret_cast<CreateParam*>(lpCreateStruct->lpCreateParams);
  if(createParam == NULL || !validateCreateParams(*createParam)) return -1;

  m_theResType = createParam->theResType;
  m_theMonitorIndex = createParam->theMonitorIndex;

  if(createItems() != ERROR_SUCCESS)
    return -1;

  return 0;
}

void CHiddenToolbar::OnDestroy()
{
  removeAllItems();
  CWnd::OnDestroy();
}

void CHiddenToolbar::OnSize(UINT nType, int cx, int cy)
{
  CWnd::OnSize(nType, cx, cy);

  rearrangne();
}


void CHiddenToolbar::OnPaint()
{
  ::CPaintDC dc(this); // device context for painting

  if(m_theDrawManager == nullptr) return;

  CRect clipBox;
  dc.GetClipBox(&clipBox);
  CRect rcClient;
  GetClientRect(&rcClient);
  CRect rcClip;
  rcClip.IntersectRect(&clipBox, &rcClient);
  CHiddenToolbar::CPaintDC toolbarDC(&dc, rcClip);

  //select font
  CFont* oldFont = nullptr;
  CFont* theToolbarFont = GetFont();
  if(theToolbarFont != NULL && theToolbarFont->m_hObject != nullptr)
    oldFont = reinterpret_cast<CFont*>(toolbarDC.SelectObject(theToolbarFont));

  //draw items
  CRect rcClipItem;
  unsigned numItems = m_items.size();
  for(unsigned i = 0; i  < numItems; ++i)
  {
    if(rcClipItem.IntersectRect(rcClip, m_items[i]->rect))
      m_theDrawManager->drawItem(*this, toolbarDC, *(m_items[i]));
  }

  //restore original font
  if(oldFont != nullptr)
    toolbarDC.SelectObject(oldFont);
}

BOOL CHiddenToolbar::OnEraseBkgnd(CDC* pDC)
{
  if(m_theDrawManager != nullptr)
  {
    m_theDrawManager->drawBK(*this, *pDC);
    return TRUE;
  }

  return CWnd::OnEraseBkgnd(pDC);
}

void CHiddenToolbar::OnLButtonDown(UINT nFlags, CPoint point)
{
  if(m_hoverItem)
  {
    m_pushedItem = m_hoverItem;
    SetCapture();
    onSelectItem(this, (void*)(m_pushedItem->uID));
  }
  Invalidate(FALSE); 
  CWnd::OnLButtonDown(nFlags, point);
}


void CHiddenToolbar::OnMouseMove(UINT nFlags, CPoint point)
{
  CWnd::OnMouseMove(nFlags, point);

  assignHoverItem(point);
}


void CHiddenToolbar::OnLButtonUp(UINT nFlags, CPoint point)
{
  CWnd::OnLButtonUp(nFlags, point);

  if(m_pushedItem != nullptr)
  {
    ReleaseCapture();
    CRect rcUpdate = m_pushedItem->rect;
    m_pushedItem = NULL;
    Invalidate(FALSE);
  }

}


LRESULT CHiddenToolbar::OnMouseLeave(WPARAM wp, LPARAM lp)
{
  CWnd::OnMouseLeave();
  if(m_hoverItem != nullptr)
  {
    CRect rcUpdate = m_hoverItem->rect;
    m_hoverItem = NULL;
    Invalidate(FALSE);
  }
  return CWnd::DefWindowProc(WM_MOUSELEAVE, wp ,lp);
}

